package com.naiterui.ehp.bs.recommend.service.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.naiterui.common.repository.page.PageParamVO;
import com.naiterui.common.repository.page.PageVO;
import com.naiterui.ehp.bp.bo.b2c.cms.MedicineBO;
import com.naiterui.ehp.bp.support.exception.BusinessException;
import com.naiterui.ehp.bp.support.utils.JsonMapper;
import com.naiterui.ehp.bp.utils.beans.BeanUtil;
import com.naiterui.ehp.bp.utils.lang.StringUtil;
import com.naiterui.ehp.bs.recommend.bean.CommonPrescription;
import com.naiterui.ehp.bs.recommend.bean.CommonPrescriptionDetail;
import com.naiterui.ehp.bs.recommend.exception.ExceptionCodes;
import com.naiterui.ehp.bs.recommend.remote.IB2cFeignClient;
import com.naiterui.ehp.bs.recommend.repository.mysql.ICommonPrescriptionDetailRepository;
import com.naiterui.ehp.bs.recommend.repository.mysql.ICommonPrescriptionRepository;
import com.naiterui.ehp.bs.recommend.service.ICommonPrescriptionService;
import com.naiterui.ehp.bs.recommend.service.IDrDiagnosisService;
import com.naiterui.ehp.bs.recommend.vo.CommonPrescriptionDetailVO;
import com.naiterui.ehp.bs.recommend.vo.CommonPrescriptionVO;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * @author yangfuya
 * @since 2021/11/8
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class CommonPrescriptionServiceImpl implements ICommonPrescriptionService {

    private final ICommonPrescriptionRepository commonPrescriptionRepository;
    private final ICommonPrescriptionDetailRepository commonPrescriptionDetailRepository;
    private final IB2cFeignClient ib2cFeignClient;
    private final IDrDiagnosisService drDiagnosisService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveOrUpdate(CommonPrescriptionVO commonPrescriptionVO) throws BusinessException {
        String name = commonPrescriptionVO.getName();
        if (StrUtil.trim(name).length() == 0 || StringUtil.containsEmoji(name)) {
            throw new BusinessException(ExceptionCodes.COMMON_PRESSCRIPTION_TEXT_NONSTANDARD);
        }
        String description = commonPrescriptionVO.getDescription();
        if (StrUtil.isNotBlank(description)
            && (StrUtil.length(description) > 100 || StringUtil.containsEmoji(description))) {
            throw new BusinessException(ExceptionCodes.COMMON_PRESSCRIPTION_TEXT_NONSTANDARD);
        }
        log.info("保存或修改常用处方: {}", JsonMapper.toJson(commonPrescriptionVO));
        Long id = commonPrescriptionVO.getId();
        if (CollUtil.isEmpty(commonPrescriptionVO.getCommonPrescriptionDetailList())) {
            throw new BusinessException(ExceptionCodes.COMMON_PRESSCRIPTION_DETAIL_IS_NULL);
        }
        List<CommonPrescription> commonPrescriptionList = commonPrescriptionRepository
            .findByDoctorIdAndNameAndDelFlag(commonPrescriptionVO.getDoctorId(), name, 0);
        if (id == null) {
            // 验证是否有已重复的
            if (CollUtil.isNotEmpty(commonPrescriptionList)) {
                throw new BusinessException(ExceptionCodes.COMMON_PRESSCRIPTION_NOT_NULL);
            }
            save(commonPrescriptionVO);
        } else {
            // 验证是否有已重复的
            for (CommonPrescription prescription : commonPrescriptionList) {
                if (!id.equals(prescription.getId())
                    && commonPrescriptionVO.getDoctorId().equals(prescription.getDoctorId())
                    && name.equals(prescription.getName())) {
                    throw new BusinessException(ExceptionCodes.COMMON_PRESSCRIPTION_NOT_NULL);
                }
            }
            update(commonPrescriptionVO, id);
        }
        List<String> diagnosisList = commonPrescriptionVO.getDiagnosisList();
        if (CollUtil.isNotEmpty(diagnosisList)) {
            // 保存常用诊断
            drDiagnosisService.updateCommonDiagnosis(commonPrescriptionVO.getDoctorId(), diagnosisList);
        }
    }

    @Override
    public PageVO<CommonPrescriptionVO> page(Long doctorId, PageParamVO pageVO) throws BusinessException {
        Page<CommonPrescription> page =
            commonPrescriptionRepository.findByDoctorIdAndDelFlag(doctorId, pageVO.getPageable(), 0);
        List<CommonPrescription> list = page.getContent();
        if (CollUtil.isEmpty(list)) {
            return PageVO.newBuilder().pageNo(pageVO.getNum()).pageSize(pageVO.getPage())
                .totalCount((int)page.getTotalElements()).result(Collections.emptyList()).build();
        }
        List<Long> idList = list.stream().map(CommonPrescription::getId).collect(Collectors.toList());
        List<CommonPrescriptionDetail> details =
            this.commonPrescriptionDetailRepository.findByCommonPrescriptionIdIn(idList);
        Map<Long, List<CommonPrescriptionDetail>> listMap = details.stream()
            .collect(Collectors.groupingBy(CommonPrescriptionDetail::getCommonPrescriptionId, Collectors.toList()));
        Set<Long> skuIdSet = details.stream().map(CommonPrescriptionDetail::getSkuId).collect(Collectors.toSet());
        Map<Long, Integer> skuPrice = ib2cFeignClient.getSkuPrice(skuIdSet);
        // 获取药品的用法用量和规格
        Set<Long> pIdSet = details.stream().map(CommonPrescriptionDetail::getProductId).collect(Collectors.toSet());
        List<MedicineBO> medicineBOS = ib2cFeignClient.getByIds(pIdSet);
        Map<Long, MedicineBO> medicineBOMap =
            medicineBOS.stream().collect(Collectors.toMap(MedicineBO::getProductId, Function.identity()));
        return PageVO.newBuilder().pageNo(pageVO.getNum()).pageSize(pageVO.getPage())
            .totalCount((int)page.getTotalElements()).result(list.stream().map(c -> {
                CommonPrescriptionVO commonPrescriptionVO = new CommonPrescriptionVO();
                BeanUtil.copyProperties(c, commonPrescriptionVO);
                commonPrescriptionVO.setCommonPrescriptionDetailList(listMap.get(c.getId()).stream().map(cd -> {
                    CommonPrescriptionDetailVO detailVO = new CommonPrescriptionDetailVO();
                    BeanUtil.copyProperties(cd, detailVO);
                    detailVO.setSalePrice(StringUtil.formatFen2YuanStr(skuPrice.get(cd.getSkuId())));
                    detailVO.setSpec(medicineBOMap.get(cd.getProductId()).getSpecification());
                    detailVO.setUsage(medicineBOMap.get(cd.getProductId()).getUsageDosage());
                    return detailVO;
                }).collect(Collectors.toList()));
                commonPrescriptionVO.setDiagnosisList(Arrays.asList(StringUtils.split(c.getDiagnosis(), ",")));
                return commonPrescriptionVO;
            }).collect(Collectors.toList())).build();
    }

    @Override
    public CommonPrescriptionVO getById(Long doctorId, Long id) throws BusinessException {
        CommonPrescription commonPrescription = commonPrescriptionRepository.findByIdAndDoctorId(id, doctorId);
        if (commonPrescription == null) {
            throw new BusinessException(ExceptionCodes.PARAM_ERROR);
        }
        CommonPrescriptionVO commonPrescriptionVO = new CommonPrescriptionVO();
        BeanUtil.copyProperties(commonPrescription, commonPrescriptionVO);
        List<CommonPrescriptionDetail> details =
            this.commonPrescriptionDetailRepository.findByCommonPrescriptionIdIn(Collections.singletonList(id));
        Set<Long> skuIdSet = details.stream().map(CommonPrescriptionDetail::getSkuId).collect(Collectors.toSet());
        Map<Long, Integer> skuPrice = ib2cFeignClient.getSkuPrice(skuIdSet);
        // 获取药品的用法用量和规格
        Set<Long> pIdSet = details.stream().map(CommonPrescriptionDetail::getProductId).collect(Collectors.toSet());
        List<MedicineBO> medicineBOS = ib2cFeignClient.getByIds(pIdSet);
        Map<Long, MedicineBO> medicineBOMap =
            medicineBOS.stream().collect(Collectors.toMap(MedicineBO::getProductId, Function.identity()));
        commonPrescriptionVO.setCommonPrescriptionDetailList(details.stream().map(cd -> {
            CommonPrescriptionDetailVO detailVO = new CommonPrescriptionDetailVO();
            BeanUtil.copyProperties(cd, detailVO);
            detailVO.setSalePrice(StringUtil.formatFen2YuanStr(skuPrice.get(cd.getSkuId())));
            detailVO.setSpec(medicineBOMap.get(cd.getProductId()).getSpecification());
            detailVO.setUsage(medicineBOMap.get(cd.getProductId()).getUsageDosage());
            return detailVO;
        }).collect(Collectors.toList()));
        commonPrescriptionVO.setDiagnosisList(Arrays.asList(StringUtils.split(commonPrescription.getDiagnosis(), ",")));
        return commonPrescriptionVO;
    }

    @Override
    public void deleteById(Long doctorId, Long id) throws BusinessException {
        CommonPrescription commonPrescription = commonPrescriptionRepository.findByIdAndDoctorId(id, doctorId);
        if (commonPrescription == null) {
            throw new BusinessException(ExceptionCodes.PARAM_ERROR);
        }
        commonPrescription.setDelFlag(1);
        commonPrescriptionRepository.update(commonPrescription);
    }

    private void save(CommonPrescriptionVO commonPrescriptionVO) {
        CommonPrescription commonPrescription = new CommonPrescription();
        BeanUtil.copyProperties(commonPrescriptionVO, commonPrescription);
        commonPrescription.setDiagnosis(getDiagnosisStr(commonPrescriptionVO.getDiagnosisList()));
        commonPrescription.setCreatedAt(new Date());
        commonPrescription.setCreatedBy("system");
        commonPrescription.setDelFlag(0);
        Long id = commonPrescriptionRepository.save(commonPrescription).getId();
        List<CommonPrescriptionDetailVO> detailList = commonPrescriptionVO.getCommonPrescriptionDetailList();
        List<CommonPrescriptionDetail> prescriptionDetailList = new ArrayList<>();
        CommonPrescriptionDetail commonPrescriptionDetail;
        for (CommonPrescriptionDetailVO detailVO : detailList) {
            commonPrescriptionDetail = new CommonPrescriptionDetail();
            BeanUtil.copyProperties(detailVO, commonPrescriptionDetail);
            commonPrescriptionDetail.setCommonPrescriptionId(id);
            commonPrescriptionDetail.setCreatedAt(new Date());
            commonPrescriptionDetail.setCreatedBy("system");
            prescriptionDetailList.add(commonPrescriptionDetail);
        }
        commonPrescriptionDetailRepository.saveAll(prescriptionDetailList);
    }

    private void update(CommonPrescriptionVO commonPrescriptionVO, Long id) throws BusinessException {
        CommonPrescription commonPrescription = commonPrescriptionRepository.get(id);
        if (commonPrescription == null) {
            throw new BusinessException(ExceptionCodes.PARAM_ERROR);
        }
        BeanUtil.copyProperties(commonPrescriptionVO, commonPrescription);
        commonPrescription.setDiagnosis(getDiagnosisStr(commonPrescriptionVO.getDiagnosisList()));
        commonPrescription.setChangedAt(new Date());
        commonPrescription.setChangedBy("system");
        commonPrescriptionRepository.update(commonPrescription);
        commonPrescriptionDetailRepository.deleteByCommonPrescriptionId(id);
        List<CommonPrescriptionDetailVO> detailList = commonPrescriptionVO.getCommonPrescriptionDetailList();
        List<CommonPrescriptionDetail> prescriptionDetailList = new ArrayList<>();
        CommonPrescriptionDetail commonPrescriptionDetail;
        for (CommonPrescriptionDetailVO detailVO : detailList) {
            commonPrescriptionDetail = new CommonPrescriptionDetail();
            BeanUtil.copyProperties(detailVO, commonPrescriptionDetail);
            commonPrescriptionDetail.setCommonPrescriptionId(id);
            commonPrescriptionDetail.setCreatedAt(new Date());
            commonPrescriptionDetail.setCreatedBy("system");
            prescriptionDetailList.add(commonPrescriptionDetail);
        }
        commonPrescriptionDetailRepository.saveAll(prescriptionDetailList);
    }

    private String getDiagnosisStr(List<String> diagnosisList) {
        if (CollUtil.isEmpty(diagnosisList)) {
            return "";
        }
        StringBuilder bd = new StringBuilder();
        for (String name : diagnosisList) {
            bd.append(name).append(",");
        }
        return bd.substring(0, bd.length() - 1);
    }
}
